package com.airbat.prize.modules.system.service.area.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.airbat.prize.common.pojo.PageResult;
import com.airbat.prize.modules.system.controller.area.vo.*;
import com.airbat.prize.modules.system.convert.area.SysAreaConvert;
import com.airbat.prize.modules.system.dal.dataobject.area.SysAreaDO;
import com.airbat.prize.modules.system.dal.dataobject.dept.SysDeptDO;
import com.airbat.prize.modules.system.dal.mysql.area.SysAreaMapper;
import com.airbat.prize.modules.system.service.area.SysAreaService;
import com.airbat.prize.mybits.mybatis.core.query.QueryWrapperX;
import com.google.common.collect.ImmutableMap;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Map;

// import static com.airbat.prize.common.exception.util.ServiceExceptionUtil.exception;
// import static com.airbat.prize.modules.sys.enums.SysErrorCodeConstants.*;

/**
 * 全国地区 Service 实现类
 *
 * @author 郑剑
 */
@Service
@Validated
@Slf4j
public class SysAreaServiceImpl implements SysAreaService {

    @Resource
    private SysAreaMapper areaMapper;

    /**
     * 地区缓存
     * key：部门编号 {@link SysDeptDO#getId()}
     * <p>
     * 这里声明 volatile 修饰的原因是，每次刷新时，直接修改指向
     */
    @SuppressWarnings("FieldCanBeLocal")
    private volatile Map<String, SysAreaDO> areaCache;
    /**
     * 缓存部门的最大更新时间，用于后续的增量轮询，判断是否有更新
     */
    private volatile Date maxUpdateTime;

    @Override
    @PostConstruct
    public synchronized void initLocalCache() {
        // 获取部门列表，如果有更新
        List<SysAreaDO> areaList = this.loadAreaIfUpdate(maxUpdateTime);
        if (CollUtil.isEmpty(areaList)) {
            return;
        }

        // 构建缓存
        ImmutableMap.Builder<String, SysAreaDO> builder = ImmutableMap.builder();
        areaList.forEach(areaDO -> {
            builder.put(areaDO.getCode(), areaDO);
        });
        // 设置缓存
        areaCache = builder.build();
        assert areaList.size() > 0; // 断言，避免告警
//        maxUpdateTime = areaList.stream().max(Comparator.comparing(BaseDO::getUpdateTime)).get().getUpdateTime();
        log.info("[initLocalCache][初始化 Area 数量为 {}]", areaList.size());
    }

    /**
     * 如果部门发生变化，从数据库中获取最新的全量部门。
     * 如果未发生变化，则返回空
     *
     * @param maxUpdateTime 当前部门的最大更新时间
     * @return 部门列表
     */
    private List<SysAreaDO> loadAreaIfUpdate(Date maxUpdateTime) {
        // 第一步，判断是否要更新。
        if (maxUpdateTime == null) { // 如果更新时间为空，说明 DB 一定有新数据
            log.info("[loadMenuIfUpdate][首次加载全量地区]");
        } else { // 判断数据库中是否有更新的部门
//            if (!areaMapper.selectExistsByUpdateTimeAfter(maxUpdateTime)) {
//                return null;
//            }
            log.info("[loadMenuIfUpdate][增量加载地区部门]");
        }
        // 第二步，如果有更新，则从数据库加载所有地区
        return areaMapper.selectList();
    }

    @Override
    public String createArea(SysAreaCreateReqVO createReqVO) {
        // 插入
        SysAreaDO area = SysAreaConvert.INSTANCE.convert(createReqVO);
        areaMapper.insert(area);
        // 返回
        return area.getCode();
    }

    @Override
    public void updateArea(SysAreaUpdateReqVO updateReqVO) {
        // 校验存在
        this.validateAreaExists(updateReqVO.getCode());
        // 更新
        SysAreaDO updateObj = SysAreaConvert.INSTANCE.convert(updateReqVO);
        areaMapper.updateById(updateObj);
    }

    @Override
    public void deleteArea(String id) {
        // 校验存在
        this.validateAreaExists(id);
        // 删除
        areaMapper.deleteById(id);
    }

    private void validateAreaExists(String id) {
        if (areaMapper.selectById(id) == null) {
//            throw exception(AREA_NOT_EXISTS);
        }
    }

    @Override
    public SysAreaDO getArea(String code) {
        return areaMapper.selectById(code);
    }

    @Override
    public SysAreaDO getAreaFromCache(String code) {
        if (!StrUtil.startWith(code, "33")) {
            SysAreaDO area = areaCache.get(code);
            if (area == null)
                return null;

            SysAreaDO province = areaCache.get(code.substring(0, 2) + "0000");
            SysAreaDO city = areaCache.get(code.substring(0, 4) + "00");
            //新做一个类，不然会导致缓存数据变化
            SysAreaDO myArea = new SysAreaDO();
            String name = "";
            if (province != null && !name.contains(province.getName()))
                name += province.getName();
            if (city != null && !name.contains(city.getName()))
                name += city.getName();
            if (area != null && !name.contains(area.getName()))
                name += area.getName();
            myArea.setName(name);
            myArea.setAreaLevel(area.getAreaLevel());
            myArea.setCode(area.getCode());
            myArea.setParentCode(area.getParentCode());
            myArea.setLat(area.getLat());
            myArea.setLng(area.getLng());
            return myArea;
        } else {
            return areaCache.get(code);
        }
    }

    @Override
    public List<SysAreaDO> getAreaList(Collection<String> ids) {
        return areaMapper.selectBatchIds(ids);
    }

    @Override
    public List<SysAreaDO> getAreaListForRange() {
//        return areaMapper.selectList(new QueryWrapperX<SysAreaDO>().isNotNull("AREA_RANGE"));
        return areaMapper.selectList(new QueryWrapperX<SysAreaDO>().likeRightIfPresent("CODE", "3303").eq("AREA_LEVEL", 3));
    }

    @Override
    public PageResult<SysAreaDO> getAreaPage(SysAreaPageReqVO pageReqVO) {
        return areaMapper.selectPage(pageReqVO);
    }

    @Override
    public List<SysAreaDO> getAreaList(SysAreaExportReqVO exportReqVO) {
        return areaMapper.selectList(exportReqVO);
    }

}
